import requests
import json
import os
import sys
from datetime import datetime
from typing import Dict, Optional, List
import pandas as pd



# 超时时间（秒）
REQUEST_TIMEOUT = 30
DOWNLOAD_TIMEOUT = 60
# 保存目录
DOWNLOAD_DIR = "./downloads"
DOWNLOAD_ROOT = "./exports"


# 全局变量定义（集中管理配置和状态）
BASE_URL = "https://caigou.chinatelecom.com.cn"
TOKEN = ""
EXPORT_PATH = ""
DOWNLOAD_ROOT = ""
LOGGER = None
START_TIME = ""
END_TIME = ""
ORDER_CODE = ""
CONTRACT_CODE=""
PAGE_SIZE = 20  # 每页订单数量
TID=""
TIME_STAMP=""
SSO_COOKIE=""
COMMON_HEADERS=""
PER=""
VENDOR_ID=""
class LOGGER:
    """日志工具类，同时输出到控制台和文件"""

    def __init__(self, log_file_path: str):
        log_dir = os.path.dirname(log_file_path)
        if not os.path.exists(log_dir):
            os.makedirs(log_dir, exist_ok=True)
        self.log_file = log_file_path

    def print(self, message: str) -> None:
        """打印并写入日志（带时间戳）"""
        print(message)
        with open(self.log_file, 'a', encoding='utf-8') as f:
            timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            f.write(f"[{timestamp}] {message}\n")


def init_global_vars(args: Optional[List[str]] = None) -> None:
    """初始化所有全局变量（集中配置）"""
    global BASE_URL, TOKEN, EXPORT_PATH, DOWNLOAD_ROOT, LOGGER,START_TIME,END_TIME,ORDER_CODE,CONTRACT_CODE,TID,TIME_STAMP,SSO_COOKIE,COMMON_HEADERS,PER,VENDOR_ID
    # 初始化日志

    # 基础配置
    BASE_URL = "https://b2b.10086.cn/sscm-vendor-api"
    EXPORT_PATH = args[5]  # 导出根路径
    CONTRACT_CODE = args[1]  # 合同编号（单独提取便于维护）
    DOWNLOAD_ROOT = os.path.join(EXPORT_PATH, f"招投标合同发票证明材料\\{CONTRACT_CODE}")
    START_TIME = args[3]
    END_TIME = args[4]
    log_file = os.path.join(EXPORT_PATH, "export_log.txt")
    LOGGER = LOGGER(log_file)
    token_data = json.loads(args[0])
    LOGGER.print(token_data)
    TID = token_data["sessionStorage"]["tid"]
    TIME_STAMP = token_data["cookie"]["timeStamp"]
    SSO_COOKIE = token_data["cookie"]["b2bSSOCookieTicket"]
    #SSO_COOKIE = "68c39c17e7bd08e5"
    store = str(token_data["sessionStorage"]["store"])
    store = json.loads(str(store))
    TOKEN = store["token"]
    TIME_STAMP=store["timeStamp"]
    PER = store["per"]
    VENDOR_ID=store["vendorId"]
    LOGGER.print(f"TID: {TID}")
    LOGGER.print(f"TIME_STAMP: {TIME_STAMP}")
    LOGGER.print(f"SSO_COOKIE: {SSO_COOKIE}")
    LOGGER.print(f"导出路径: {EXPORT_PATH}")
    LOGGER.print(f"合同编号: {CONTRACT_CODE}")
    LOGGER.print(f"下载根目录: {DOWNLOAD_ROOT}")
    LOGGER.print(f"开始时间: {START_TIME}")
    LOGGER.print(f"结束时间: {END_TIME}")
    LOGGER.print(f"Token: {TOKEN}")

    # 公共配置
    COMMON_HEADERS = {
        "Accept": "application/json, text/plain, */*",
        "Accept-Language": "zh-CN,zh;q=0.9,ru;q=0.8",
        "Authorization": f"Bearer {TOKEN}",
        "Content-Type": "application/json;charset=UTF-8",
        "Origin": "https://b2b.10086.cn",
        "Referer": "https://b2b.10086.cn/sscm-vendor/",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
        "local-host-ip": "null",
        "per": PER,
        "sec-ch-ua": "\"Not)A;Brand\";v=\"8\", \"Chromium\";v=\"138\", \"Google Chrome\";v=\"138\"",
        "sec-ch-ua-mobile": "?0",
        "sec-ch-ua-platform": "\"Windows\"",
        "tid": TID,
        "uid": "1613759816125988861",
        "Cookie": f"timeStamp={TIME_STAMP}; b2bSSOCookieTicket={SSO_COOKIE}"
    }
    # 初始化Token（实际使用时建议从安全渠道获取）
    #TOKEN = "eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjBjN2Q1YWM1LWFjM2MtNDNiNS04Nzc5LWUxOTU5ZDE0ZWQ1YSJ9.N-7BzXFbQZWneVeBvAspz6k9up5qbjNn4GLgj3-wuuCO6skrmI6uWZH7sIejcfV0j9eMC9h3ow9LnEcq2xmm6g"

    # 初始化下载根目录
    try:
        os.makedirs(DOWNLOAD_ROOT, exist_ok=True)
        LOGGER.print(f"下载根目录: {os.path.abspath(DOWNLOAD_ROOT)}")
    except OSError as e:
        LOGGER.print(f"创建下载目录失败: {str(e)}")




# -------------------------- 工具函数 --------------------------
def send_post_request(url: str, payload: Dict, timeout: int = REQUEST_TIMEOUT, verify: bool = False) -> Optional[Dict]:
    """
    封装POST请求通用逻辑
    :param url: 请求URL
    :param payload: 请求体数据
    :param timeout: 超时时间（秒）
    :param verify: 是否验证SSL证书
    :return: 解析后的JSON响应（字典），失败返回None
    """
    try:
        response = requests.post(
            url=url,
            json=payload,
            headers=COMMON_HEADERS,
            timeout=timeout,
            verify=verify
        )
        response.raise_for_status()  # 触发HTTP错误（4xx/5xx）
        return response.json()
    except requests.exceptions.HTTPError as e:
        LOGGER.print(f"POST请求HTTP错误 [{url}]：状态码={e.response.status_code}，响应={e.response.text[:500]}")
    except requests.exceptions.RequestException as e:
        LOGGER.print(f"POST请求网络异常 [{url}]：{str(e)}")
    except json.JSONDecodeError:
        LOGGER.print(f"POST请求响应不是有效JSON [{url}]")
    except Exception as e:
        LOGGER.print(f"POST请求未知异常 [{url}]：{str(e)}")
    return None


# -------------------------- 业务函数 --------------------------
def query_po_headers(orderNo: str = "") -> Optional[Dict]:
    """
    查询采购订单头部信息
    :param contract_code: 合同编号（可选，用于筛选）
    :return: 订单列表数据（字典），失败返回None
    """
    url = "https://b2b.10086.cn/sscm-vendor-api/api-data-es/product/poHeader/queryAllPo"
    payload = {
        "tableData": [],
        "vendorId": VENDOR_ID,
        "vendorName": None,
        "dateStart": "",
        "dateEnd": "",
        "approvedDate": "",
        "current": 1,
        "size": 10,
        "contractCode": "",
        "segment1": orderNo
    }
    return send_post_request(url, payload)


def list_files_by_attr(attr2: str, tenant_id: str) -> Optional[Dict]:
    """
    根据属性查询关联文件列表
    :param attr2: 属性2（通常为订单UUID）
    :param tenant_id: 租户ID
    :return: 文件列表数据（字典），失败返回None
    """
    url = "https://b2b.10086.cn/sscm-vendor-api/api-file/file/listByAttr"
    payload = {
        "attr1": "PO_HEADERS_ALL",
        "attr2": attr2,
        "attr3": "PO_SEAL",
        "tenantId": tenant_id,
        "isUpload": False
    }
    return send_post_request(url, payload)


def download_file(file_id: str, uuid: str, save_dir:str, filename: str = "") -> Optional[str]:
    """
    下载文件并保存到本地
    :param file_id: 文件ID
    :param uuid: 文件UUID
    :param save_dir: 保存目录
    :param filename: 保存的文件名（可选，为空则自动生成）
    :return: 保存路径（字符串），失败返回None
    """
    url = "https://b2b.10086.cn/sscm-vendor-api/api-file/file/getFile"
    payload = {"fileId": file_id, "uuid": uuid}



    # 处理文件名（为空则生成默认名）
    if not filename:
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        filename = f"file_{timestamp}_{file_id}.pdf"
    save_path = os.path.join(save_dir, filename)

    try:
        response = requests.post(
            url=url,
            json=payload,
            headers=COMMON_HEADERS,
            timeout=DOWNLOAD_TIMEOUT,
            verify=False,
            stream=True
        )
        response.raise_for_status()

        # 流式写入文件
        with open(save_path, "wb") as f:
            for chunk in response.iter_content(chunk_size=1024 * 1024):  # 1MB分块
                if chunk:
                    f.write(chunk)

        LOGGER.print(f"文件下载成功：{save_path}")
        return save_path
    except requests.exceptions.HTTPError as e:
        LOGGER.print(f"文件下载HTTP错误 [{file_id}]：状态码={e.response.status_code}，响应={e.response.text[:500]}")
    except requests.exceptions.RequestException as e:
        LOGGER.print(f"文件下载网络异常 [{file_id}]：{str(e)}")
    except Exception as e:
        LOGGER.print(f"文件下载未知异常 [{file_id}]：{str(e)}")

    # 清理可能的不完整文件
    if os.path.exists(save_path):
        os.remove(save_path)
    return None


def export_excel(query_params: Dict) -> Optional[str]:
    """
    导出Excel文件
    :param query_params: 查询参数（字典）
    :return: 保存路径（字符串），失败返回None
    """
    url = "https://b2b.10086.cn/sscm-vendor-api/api-data-es/exceldpGeneral/exportExcel"

    # 复用公共请求头，仅修改必要字段
    headers = COMMON_HEADERS.copy()
    headers["Accept"] = "*/*"  # 覆盖原Accept，适配Excel下载
    headers["tid"] = TID  # 按原curl更新tid

    payload = {
        "excelMapper": "ExcelMapper-poHeaderList",
        "targetFileName": "订单列表",
        "queryParams": json.dumps(query_params)
    }

    # 确保保存目录存在
    os.makedirs(DOWNLOAD_ROOT, exist_ok=True)

    try:
        response = requests.post(
            url=url,
            json=payload,
            headers=headers,
            timeout=DOWNLOAD_TIMEOUT,
            verify=False,
            stream=True
        )
        response.raise_for_status()

        # 解析文件名（从响应头或生成默认）
        content_disposition = response.headers.get("Content-Disposition", "")
        if "filename=" in content_disposition:
            filename = content_disposition.split("filename=")[-1].strip('"')
            if filename.startswith("UTF-8''"):
                filename = requests.utils.unquote(filename[7:])
        else:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            filename = f"订单列表_{timestamp}.xlsx"

        save_path = os.path.join(DOWNLOAD_ROOT, filename)

        # 流式写入文件
        with open(save_path, "wb") as f:
            for chunk in response.iter_content(chunk_size=1024 * 1024):
                if chunk:
                    f.write(chunk)

        LOGGER.print(f"Excel导出成功：{save_path}")
        return save_path
    except requests.exceptions.HTTPError as e:
        LOGGER.print(f"Excel导出HTTP错误：状态码={e.response.status_code}，响应={e.response.text[:500]}")
    except requests.exceptions.RequestException as e:
        LOGGER.print(f"Excel导出网络异常：{str(e)}")
    except Exception as e:
        LOGGER.print(f"Excel导出未知异常：{str(e)}")

    return None

#根据条件导出结算单excel
def process_excel_data(excel_path: str) -> None:
    """处理Excel文件，提取结算单和订单信息并下载对应文件"""
    if not os.path.exists(excel_path):
        LOGGER.print(f"错误：Excel文件不存在 - {excel_path}")
        return

    try:
        # 读取Excel数据
        df = pd.read_excel(excel_path, sheet_name=0)
        required_columns = ["订单号", "单据状态", "订单名称"]

        # 验证表头
        for col in required_columns:
            if col not in df.columns:
                LOGGER.print(f"错误：Excel缺少必要字段 - {col}")
                return

        # 处理每行数据
        total_rows = len(df)
        skipped_count = 0

        for index, row in df.iterrows():
            if should_stop():
                print("")
                break
            row_num = index + 2  # Excel行号（含表头）
            orderStatus = row["单据状态"].strip() if not pd.isna(row["单据状态"]) else ""
            orderNo = str(row["订单号"]).strip() if not pd.isna(row["订单号"]) else ""
            LOGGER.print("=============="+orderNo)
            # 跳过无效金额
            if pd.isna(orderStatus) or orderStatus == '已取消':
                skipped_count += 1
                LOGGER.print(f"跳过行{row_num}：订单号{orderNo}已取消")
                continue

            # 提取关键信息


            po_headers = query_po_headers(orderNo)
            # 验证订单数据结构
            if not (po_headers and "data" in po_headers and "content" in po_headers["data"]):
                LOGGER.print("未获取到有效的采购订单头部信息，终止流程")
                return
            po_headers_content = po_headers["data"]["content"][0]
            attr2 = po_headers_content["uuid"]
            tenant_id = po_headers_content["tenantId"]
            file_list = list_files_by_attr(attr2, tenant_id)

            if not (file_list and "data" in file_list and file_list["data"]):
                LOGGER.print("未查询到关联文件，跳过该记录")
                #如果没有附件 创建一个带空的文件夹以表示
                save_dir = os.path.join(DOWNLOAD_ROOT, "(空)" + orderNo)
                # 确保保存目录存在
                os.makedirs(save_dir, exist_ok=True)
                continue
            file_info = file_list["data"][0]
            save_dir = os.path.join(DOWNLOAD_ROOT, orderNo)
            # 确保保存目录存在
            os.makedirs(save_dir, exist_ok=True)

            try:
                file_id = file_info["fileId"]
                file_uuid = file_info["uuid"]
                file_name = file_info["filename"]
                LOGGER.print(f"准备下载文件: {file_name} (fileId: {file_id})")
            except KeyError as e:
                LOGGER.print(f"文件信息格式异常，缺少字段: {e}，跳过该文件")
                continue

            # 执行下载
            download_file(
                file_id=file_id,
                uuid=file_uuid,
                save_dir=save_dir,
                filename=file_name
            )

        # 输出统计结果
        LOGGER.print(f"\n处理完成：共{total_rows}行，有效{total_rows - skipped_count}行，跳过{skipped_count}行")

    except Exception as e:
        LOGGER.print(f"Excel处理失败：{str(e)}")

# -------------------------- 主函数 --------------------------
def main():

    # 获取命令行参数
    args = sys.argv[1:]
    if len(args) < 7:
        print(
            "参数错误！使用方式: python script.py [token] [框架合同编号] [商品名称] [开始时间] [结束时间] [导出路径] [目录结构标识]")
        sys.exit(1)
    """主函数：初始化并执行流程"""
    init_global_vars(args)

    query_params = {
        "segment1": "",
        "contractCode": CONTRACT_CODE,
        "vendorId": VENDOR_ID,
        "dateStart": START_TIME,
        "dateEnd": END_TIME,
        "approvedDate": ""
    }
    path = export_excel(query_params)
    process_excel_data(path)



if __name__ == "__main__":
    main()